home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Assembly Programming Journal / apj_1.txt next >
Encoding:
Text File  |  2000-05-25  |  74.4 KB  |  1,861 lines

  1. ::/ \::::::.
  2. :/___\:::::::.
  3. /|    \::::::::.
  4. :|   _/\:::::::::.
  5. :| _|\  \::::::::::.                                                Oct/Nov 98
  6. :::\_____\::::::::::.                                               Issue    1
  7. ::::::::::::::::::::::.........................................................
  8.  
  9.             A S S E M B L Y   P R O G R A M M I N G   J O U R N A L
  10.                       http://asmjournal.freeservers.com
  11.                            asmjournal@mailcity.com
  12.  
  13.  
  14.  
  15.  
  16. T A B L E   O F   C O N T E N T S
  17. ----------------------------------------------------------------------
  18. Introduction...................................................mammon_
  19.  
  20. "VGA Programming in Mode 13h".............................Lord Lucifer
  21.  
  22. "SMC Techniques: The Basics"...................................mammon_
  23.  
  24. "Going Ring0 in Windows 9x".....................................Halvar
  25.  
  26. Column: Win32 Assembly Programming
  27.     "The Basics"..............................................Iczelion
  28.     "MessageBox"..............................................Iczelion
  29.  
  30. Column: The C standard library in Assembly
  31.     "_itoa, _ltoa and _ultoa"...................................Xbios2
  32.  
  33. Column: The Unix World
  34.     "x86 ASM Programming for Linux"............................mammon_
  35.  
  36. Column: Issue Solution
  37.     "11-byte Solution"..........................................Xbios2
  38. ----------------------------------------------------------------------
  39.       +++++++++++++++++++++++Issue Challenge++++++++++++++++++++
  40.       Write a program that displays its command line in 11 bytes
  41. ----------------------------------------------------------------------
  42.  
  43.  
  44.  
  45.  
  46. ::/ \::::::.
  47. :/___\:::::::.
  48. /|    \::::::::.
  49. :|   _/\:::::::::.
  50. :| _|\  \::::::::::.
  51. :::\_____\:::::::::::..............................................INTRODUCTION
  52.                                                                      by mammon_
  53.  
  54.  
  55. Welcome to the first issue of Assembly Programming Journal. Assembly language
  56. has become of renewed interest to a lot of programmers, in what must be a
  57. backlash to the surge of poor-quality RAD-developed programs (from Delphi, VB,
  58. etc) released as free/shareware over the past few years. Assembly language
  59. code is tight, fast, and often well-coded -- you tend to find fewer
  60. inexperienced coders writing in assembly language than you do writing in, say,
  61. Visual Basic.
  62.  
  63. The selection of articles is somewhat eclectic and should demonstrate the
  64. focus of this magazine: i.e., it targets the assembly-language programming
  65. community, not any particular type of coding such as Win32, virus, or demo
  66. programmimg. As the magazine is newly born and much of its purpose may seem
  67. unclear, I will devote the rest of this column to the most common questions I
  68. have received via email regarding the mag.
  69.  
  70.  
  71. How often will an issue be released?
  72. ------------------------------------
  73. Barring hazard, an issue will be released every other month.
  74.  
  75.  
  76. What types of articles will be accepted?
  77. ----------------------------------------
  78. Anything to do with assembly language. Obviously repeats of previously
  79. presented material are not necessary unless they enhance or clarify the
  80. earlier material. The focus will be on Intel x86 instruction sets; however
  81. coding for other processors is acceptable (though out of courtesy it would be
  82. good point to an x86 emulator for the processor you write on).
  83.  
  84. Personally I am looking for articles on the areas of asembly language that
  85. interest me: code optimization, demo/graphics programming, virus coding, unix
  86. and other-OS asm coding, and OS-internals.
  87.  
  88. Demos (with source) and quality ASCII art (for issue covers, column logos,
  89. etc) are especially welcome.
  90.  
  91.  
  92. For what level of coding experience is the mag intended?
  93. --------------------------------------------------------
  94. The magazine is intended to appeal to asm coders of all levels. Each issue
  95. will contain mostly beginner and intermediate level code/techniques, as these
  96. will by nature be of the greatest demand; however one of the goals of APJ is
  97. to include enough advanced material to make the magazine appeal to "pros" as
  98. well.
  99.  
  100.  
  101. How will the mag be distributed?
  102. --------------------------------
  103. Assembly Programming Journal has its own web page at
  104. http://asmjournal.freeservers.com
  105. which will contain the current issue and an archive of previous issues. The
  106. page also contains a guestbook and a disucssion board for article writers and
  107. readers.
  108.  
  109. An email subscription may be obtained by sending an email to
  110. asmjournal@mailcity.com
  111. with the subject "SUBSCRIBE"; starting with the next issue, Assembly
  112. Programming Journal will be emailed to the address you sent the mail from.
  113.  
  114.  
  115. Wrap-up
  116. -------
  117. That's the bulk of the "faq". Enjoy the mag!
  118.  
  119.  
  120. ::/ \::::::.
  121. :/___\:::::::.
  122. /|    \::::::::.
  123. :|   _/\:::::::::.
  124. :| _|\  \::::::::::.
  125. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  126.                                                     VGA Programming in Mode 13h
  127.                                                     by Lord Lucifer
  128.  
  129.  
  130. This article will describe how to program VGA graphics Mode 13h using assembly
  131. language.  Mode 13h is the 320x200x256 graphics mode, and is fast and very
  132. convenient from a programmer's perspective.
  133.  
  134. The video buffer begins at address A000:0000 and ends at address A000:F9FF.
  135. This means the buffer is 64000 bytes long and that each pixel in mode 13h is
  136. represented by one byte.
  137.  
  138. It is easy to set up mode 13h and the video buffer in assembly language:
  139.  
  140.         mov     ax,0013h        ; Int 10 - Video BIOS Services
  141.         int     10h             ; ah = 00 - Set Video Mode
  142.                                 ; al = 13 - Mode 13h (320x200x256)
  143.  
  144.         mov     ax,0A000h       ; point segment register es to A000h
  145.         mov     es,ax           ; we can now access the video buffer as
  146.                                 ; offsets from register es
  147.  
  148. At the end of your program, you will probably want to restore the text mode.
  149. Here's how:
  150.  
  151.         mov     ax,0003h        ; Int 10 - Video BIOS Services
  152.         int     10h             ; ah = 00 - Set Video Mode
  153.                                 ; al = 03 - Mode 03h (80x25x16 text)
  154.  
  155. Accessing a specific pixel int the buffer is also very easy:
  156.  
  157.                                 ; bx = x coordinate
  158.                                 ; ax = y coordinate
  159.         mul     320             ; multiply y coord by 320 to get row
  160.         add     ax,bx           ; add this with the x coord to get offset
  161.  
  162.         mov     cx,es:[ax]      ; now pixel x,y can be accessed as es:[ax]
  163.  
  164. Hmm... That was easy, but that multiplication is slow and we should get rid of
  165. it.  That's easy to do too, simply by using bit shifting instead of multiplica-
  166. tion. Shifting a number to the left is the same as multiplying by 2. We want to
  167. multiply by 320, which is not a multiple of 2, but 320 = 256 + 64, and 256 and
  168. 64 are both even multiples of 2.  So a faster way to access a pixel is:
  169.  
  170.                                 ; bx = x coordinate
  171.                                 ; ax = y coordinate
  172.         mov     cx,bx           ; copy bx to cx, to save it temporatily
  173.         shl     cx,8            ; shift left by 8, which is the same as
  174.                                 ; multiplying by 2^8 = 256
  175.         shl     bx,6            ; now shift left by 6, which is the same as
  176.                                 ; multiplying by 2^6 = 64
  177.         add     bx,cx           ; now add those two together, whis is
  178.                                 ; effectively multiplying by 320
  179.         add     ax,bx           ; finally add the x coord to this value
  180.         mov     cx,es:[ax]      ; now pixel x,y can be accessed as es:[ax]
  181.  
  182. Well, the code is a little bit longer and looks more complicated, but I can
  183. guarantee it's much faster.
  184.  
  185. To plot colors, we use a color look-up table.  This look-up table is a 768
  186. (3x256) array.  Each index of the table is really the offset index*3. The 3
  187. bytes at each index hold the corresponding values (0-63) of the red, green,
  188. and blue components.  This gives a total of 262144 total possible colors.
  189. However, since the table is only 256 elements big, only 256 different colors
  190. are possible at a given time.
  191.  
  192. Changing the color palette is accomplished through the use of the I/O ports of
  193. the VGA card:
  194.  
  195.         Port 03C7h is the Palette Register Read port.
  196.         Port 03C8h is the Palette Register Write port
  197.         Port 03C9h is the Palette Data port
  198.  
  199. Here is how to change the color palette:
  200.  
  201.                                 ; ax = palette index
  202.                                 ; bl = red component (0-63)
  203.                                 ; cl = green component (0-63)
  204.                                 ; dl = blue component (0-63)
  205.  
  206.         mov     dx,03C8h        ; 03c8h = Palette Register Write port
  207.         out     dx,ax           ; choose index
  208.  
  209.         mov     dx,03C9h        ; 03c8h = Palette Data port
  210.         out     dx,al
  211.         mov     bl,al           ; set red value
  212.         out     dx,al
  213.         mov     cl,al           ; set green value
  214.         out     dx,al
  215.         mov     dl,al           ; set blue value
  216.  
  217. Thats all there is to it.  Reading the color palette is similar:
  218.  
  219.                                 ; ax = palette index
  220.                                 ; bl = red component (0-63)
  221.                                 ; cl = green component (0-63)
  222.                                 ; dl = blue component (0-63)
  223.  
  224.         mov     dx,03C7h        ; 03c7h = Palette Register Read port
  225.         out     dx,ax           ; choose index
  226.  
  227.         mov     dx,03C9h        ; 03c8h = Palette Data port
  228.         in      al,dx
  229.         mov     bl,al           ; get red value
  230.         in      al,dx
  231.         mov     cl,al           ; get green value
  232.         in      al,dx
  233.         mov     dl,al           ; get blue value
  234.  
  235. Now all we need to know is how to plot a pixel of a certain color at a certain
  236. location.  Its very easy, given what we already know:
  237.  
  238.                                 ; bx = x coordinate
  239.                                 ; ax = y coordinate
  240.                                 ; dx = color (0-255)
  241.         mov     cx,bx           ; copy bx to cx, to save it temporatily
  242.         shl     cx,8            ; shift left by 8, which is the same as
  243.                                 ; multiplying by 2^8 = 256
  244.         shl     bx,6            ; now shift left by 6, which is the same as
  245.                                 ; multiplying by 2^6 = 64
  246.         add     bx,cx           ; now add those two together, whis is
  247.                                 ; effectively multiplying by 320
  248.         add     ax,bx           ; finally add the x coord to this value
  249.         mov     es:[ax],dx      ; copy color dx into memory location
  250.                                 ; thats all there is to it
  251.  
  252. Ok, we now know how to set up Mode 13h, set up the video buffer, plot a pixel,
  253. and edit the color palette.
  254.  
  255. My next article will go on to show how to draw lines, utilize the vertical
  256. retrace for smoother rendering, and anything else I can figure out by that
  257. time...
  258.  
  259.  
  260. ::/ \::::::.
  261. :/___\:::::::.
  262. /|    \::::::::.
  263. :|   _/\:::::::::.
  264. :| _|\  \::::::::::.
  265. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  266.                                                      SMC Techniques: The Basics
  267.                                                      by mammon_
  268.  
  269.  
  270. One of the benefits of coding in assembly language is that you have the option
  271. to be as tricky as you like: the binary gymnastics of viral code demonstrate
  272. this above all else. One of the viral "tricks" that has made its way into
  273. standard protection schemes is SMC: self-modifying code.
  274.  
  275. In this article I will not be discussing polymorphic viruses or mutation
  276. engines; I will not go into any specific software protection scheme, or cover
  277. any anti-debugger/anti-disassembler tricks, or even touch on the matter of the
  278. PIQ. This is intended to be a simple primer on self-modifying code, for those
  279. new to the concept and/or implementation.
  280.  
  281.  
  282. Episode 1: Opcode Alteration
  283. ----------------------------
  284. One of the purest forms of self-modifying code is to change the value of an
  285. instruction before it is executed...sometimes as the result of a comparison,
  286. and sometimes to hide the code from prying eyes. This technique essentially
  287. has the following pattern:
  288.         mov reg1, code-to-write
  289.         mov [addr-to-write-to], reg1
  290. where 'reg1' would be any register, and where '[addr-to-write-to]' would be a
  291. pointer to the address to be changed. Note that 'code-to-write- would ideally
  292. be an instruction in hexadecimal format, but by placing the code elsewhere in
  293. the program--in an uncalled subroutine, or in a different segment--it is
  294. possible to simply transfer the compiled code from one location to another via
  295. indirect addressing, as follows:
  296.           call changer
  297.           mov dx, offset [string]     ;this will be performed but ignored
  298. label:    mov ah, 09                  ;this will never be perfomed
  299.           int 21h                     ;this will exit the program
  300.           ....
  301. changer:  mov di, offset to_write     ;load address of code-to-write in DI
  302.           mov byte ptr [label], [di]  ;write code to location 'label:'
  303.           ret                         ;return from call
  304. to_write: mov ah, 4Ch                 ;terminate to DOS function
  305.  
  306. this small routine will cause the program to exit, though in a disassembler it
  307. at first appears to be a simple print string routine. Note that by combining
  308. indirect addressing with loops, entire subroutines--even programs--can be
  309. overwritten, and the code to be written--which may be stored in the program as
  310. data--can be encrypted with a simple XOR to disguise it from a disassembler.
  311.  
  312. The following is a complete asm program to demonstrate patching "live" code;
  313. it asks the user for a password, then changes the string to be printed
  314. depending on whether or not the password is correct:
  315. ; smc1.asm ==================================================================
  316. .286
  317. .model small
  318. .stack 200h
  319. .DATA
  320. ;buffer for Keyboard Input, formatted for easy reference:
  321. MaxKbLength  db 05h
  322. KbLength     db 00h
  323. KbBuffer     dd 00h
  324.  
  325. ;strings: note the password is not encrypted, though it should be...
  326. szGuessIt        db     'Care to guess the super-secret password?',0Dh,0Ah,'$'
  327. szString1        db     'Congratulations! You solved it!',0Dh,0Ah, '$'
  328. szString2        db     'Ah, damn, too bad eh?',0Dh,0Ah,'$'
  329. secret_word      db     "this"
  330.  
  331. .CODE
  332. ;===========================================
  333. start:
  334.         mov     ax,@data                ; set segment registers
  335.         mov     ds, ax                  ; same as "assume" directive
  336.         mov     es, ax
  337.         call Query                      ; prompt user for password
  338.         mov     ah, 0Ah                 ; DOS 'Get Keyboard Input' function
  339.         mov     dx, offset MaxKbLength  ; start of buffer
  340.         int     21h
  341.         call Compare                    ; compare passwords and patch
  342. exit:
  343.         mov ah,4ch                      ; 'Terminate to DOS' function
  344.         int 21h
  345. ;===========================================
  346. Query            proc
  347.         mov  dx, offset szGuessIt       ; Prompt string
  348.         mov  ah, 09h                    ; 'Display String' function
  349.         int  21h
  350.         ret
  351. Query            endp
  352. ;===========================================
  353. Reply            proc
  354. PatchSpot:
  355.         mov  dx, offset szString2       ; 'You failed' string
  356.         mov  ah, 09h                    ; 'Display String' function
  357.         int  21h
  358.         ret
  359. Reply            endp
  360. ;===========================================
  361. Compare            proc
  362.         mov     cx, 4                   ; # of bytes in password
  363.         mov     si, offset KbBuffer     ; start of password-input in Buffer
  364.         mov     di, offset secret_word  ; location of real password
  365.         rep cmpsb                       ; compare them
  366.         or cx, cx                       ; are they equal?
  367.         jnz     bad_guess               ; nope, do not patch
  368.         mov word ptr cs:PatchSpot[1], offset szString1  ;patch to GoodString
  369. bad_guess:
  370.         call Reply                      ; output string to display result
  371.         ret
  372. Compare            endp
  373. end     start
  374. ; EOF =======================================================================
  375.  
  376.  
  377. Episode 2: Encryption
  378. ---------------------
  379. Encryption is undoubtedly the most common form of SMC code used today. It is
  380. used by packers and exe-encryptors to either compress or hide code, by viruses
  381. to disguise their contents, by protection schemes to hide data. The basic
  382. format of encryption SMC would be:
  383.         mov reg1, addr-to-write-to
  384.         mov reg2, [reg1]
  385.         manipulate reg2
  386.         mov [reg1], reg2
  387. where 'reg1' would be a register containing the address (offset) of the
  388. location to write to, and reg2 would be a temporary register which loads the
  389. contents of the first and then modifies them via mathematical (ROL) or logical
  390. (XOR) operations. The address to be patched is stored in reg1, its contents
  391. modified within reg2, and then written back to the original location still
  392. stored in reg1.
  393.  
  394. The program given in the preceding section can be modified so that it
  395. unencrypts the password by overwriting it (so that it remains unencrypted
  396. until the program is terminated) by first changing the 'secret_word' value as
  397. follows:
  398. secret_word      db     06Ch, 04Dh, 082h, 0D0h
  399.  
  400. and then by changing the 'Compare' routine to patch the 'secret_word' location
  401. in the data segment:
  402. ;===========================================
  403. magic_key        db     18h, 25h, 0EBh, 0A3h ;not very secure!
  404.  
  405. Compare            proc    ;Step 1: Unencrypt password
  406.         mov     al, [magic_key]              ; put byte1 of XOR mask in al
  407.         mov     bl, [secret_word]            ; put byte1 of password in bl
  408.         xor     al, bl
  409.         mov     byte ptr secret_word, al     ; patch byte1 of password
  410.         mov     al, [magic_key+1]            ; put byte2 of XOR mask in al
  411.         mov     bl, [secret_word+1]          ; put byte2 of password in bl
  412.         xor     al, bl
  413.         mov     byte ptr secret_word[1], al  ; patch byte2 of password
  414.         mov     al, [magic_key+2]            ; put byte3 of XOR mask in al
  415.         mov     bl, [secret_word+2]          ; put byte3 of password in bl
  416.         xor     al, bl
  417.         mov     byte ptr secret_word[2], al  ; patch byte3 of password
  418.         mov     al, [magic_key+3]            ; put byte4 of XOR mask in al
  419.         mov     bl, [secret_word+3]          ; put byte4 of password in bl
  420.         xor     al, bl
  421.         mov     byte ptr secret_word[3], al  ; patch byte4 of password
  422.         mov     cx, 4      ;Step 2: Compare Passwords...no changes from here
  423.         mov     si,offset KbBuffer
  424.         mov     di, offset secret_word
  425.         rep     cmpsb
  426.         or      cx, cx
  427.         jnz     bad_guess
  428.         mov     word ptr cs:PatchSpot[1], offset szString1
  429. bad_guess:
  430.         call Reply
  431.         ret
  432. Compare            endp
  433.  
  434. Note the addition of the 'magic_key' location which contains the XOR mask for
  435. the password. This whole thing could have been made more sophisticated with a
  436. loop, but with only four bytes the above speeds debugging time (and, thereby,
  437. article-writing time). Note how the password is loaded, XORed, and re-written
  438. one byte at a time; using 32-bit code, the whole (dword) password could be
  439. written, XORed and an re-written at once.
  440.  
  441.  
  442. Episode 3. Fooling with the stack
  443. ---------------------------------
  444. This is a trick I learned while decompiling some of SunTzu's code. What
  445. happens here is pretty interesting: the stack is moved into the code segment
  446. of the program, such that the top of the stack is set to the first address to
  447. be patched (which, BTW, should be the one closest to the end of the program
  448. due to the way the stack works); the byte at this address is the POPed into a
  449. register, manipulated, and PUSHed back to its original location. The stack
  450. pointer (SP) is then decremented so that the next address to be patched (i
  451. byte lower in memory) is now at the top of the stack.
  452.  
  453. In addition, the bytes are being XORed with a portion of the program's own
  454. code, which disguises somewhat the actual value of the XOR mask. In the
  455. following code, I chose to use the bytes from Start: (200h when compiled)
  456. up to --but not including-- Exit: (214h when compiled; Exit-1 = 213h).
  457. However, as with SunTzu's original code I kept the "reverse" sequence of the
  458. XOR mask such that byte 213h is the first byte of the XOR mask, and byte 200h
  459. is the last. After some experimentation I found this was the easiest way to
  460. sync a patch program--or a hex editor--to the stack-manipulative code; since
  461. the stack moves backwards (a forward-moving stack is more trouble than it is
  462. worth), using a "reverse" XOR mask allows both filepointers in a patcher to be
  463. INCed or DECed in sync.
  464.  
  465. Why is this an issue? Unlike the previous two examples, the following does not
  466. contain the encrypted version of the code-to-be-patched. It simply contains
  467. the source code which, when compiled, results in the unencrypted bytes which
  468. are then run through the XOR routine, encrypted, and then executed (which, if
  469. you have followed thus far, will immediately demonstrate to be no good...
  470. though it is a fantastic way of crashing the DOS VM!).
  471.  
  472. Once the program is compiled you must either patch the bytes-to-be-decrypted
  473. manually, or write a patcher to do the job for you. The former is more
  474. expedient, the latter is more certain and is a must if you plan on maintaining
  475. the code. In the following example I have embedded 2 CCh's (Int3) in the code
  476. at the fore and aft end of the bytes-to-be-decrypted section; a patcher need
  477. simply search for these, count the bytes in between, and then XOR with the
  478. bytes between 200-213h.
  479.  
  480. Once again, this sample is a continuation of the previous example. In it, I
  481. have written a routine to decrypt the entire 'Compare' routine of the previous
  482. section by XORing it with the bytes between 'Start' and 'Exit'. This is
  483. accomplished by seeting the stack segment equal to the code segment, then
  484. setting the stack pointer equal to the end (highest) address of the code to be
  485. modified. A byte is POPed from the stack (i.e. it's original location), XORed,
  486. and PUSHed back to its original location. The next byte is loaded by
  487. decrementing the stack pointer. Once all of the code it decrypted, control is
  488. returned to the newly-decrypted 'Compare' routine and normal execution
  489. resumes.
  490.  
  491. ;===========================================
  492. magic_key        db     18h, 25h, 0EBh, 0A3h
  493.  
  494. Compare            proc
  495.          mov cx, offset EndPatch[1]    ;start addr-to-write-to + 1
  496.          sub cx, offset patch_pwd      ;end addr-to-write-to
  497.          mov ax, cs
  498.          mov dx, ss                    ;save stack segment--important!
  499.          mov ss, ax                    ;set stack segment to code segment
  500.          mov bx, sp                    ;save stack pointer
  501.          mov sp, offset EndPatch       ;start addr-to-write-to
  502.          mov si, offset Exit-1         ;start sddr of XOR mask
  503. XorLoop:
  504.          pop ax                        ;get byte-to-patch into AL
  505.          xor al, [si]                  ;XOR al with XorMask
  506.          push ax                       ;write byte-to-patch back to memory
  507.          dec sp                        ;load next byte-to-patch
  508.          dec si                        ;load next byte of XOR mask
  509.          cmp si, offset Start          ;end sddr of XOR mask
  510.          jae GoLoop                    ;if not at end of mask, keep going
  511.          mov si, offset Exit-1         ;start XOR mask over
  512. GoLoop:
  513.          loop XorLoop                  ;XOR next byte
  514.          mov sp, bx                    ;restore stack pointer
  515.          mov ss, dx                    ;restore stack segment
  516.          jmp    patch_pwd
  517.          db     0CCh,0CCh              ;Identifcation mark: START
  518. patch_pwd:                             ;no changes from here
  519.         mov     al, [magic_key]
  520.         mov     bl, [secret_word]
  521.         xor     al, bl
  522.         mov     byte ptr secret_word, al
  523.         mov     al, [magic_key+1]
  524.         mov     bl, [secret_word+1]
  525.         xor     al, bl
  526.         mov     byte ptr secret_word[1], al
  527.         mov     al, [magic_key+2]
  528.         mov     bl, [secret_word+2]
  529.         xor     al, bl
  530.         mov     byte ptr secret_word[2], al
  531.         mov     al, [magic_key+3]
  532.         mov     bl, [secret_word+3]
  533.         xor     al, bl
  534.         mov     byte ptr secret_word[3], al
  535. ;compare password
  536.         mov     cx, 4
  537.         mov     si, offset KbBuffer
  538.         mov     di, offset secret_word
  539.         rep cmpsb
  540.         or cx, cx
  541.         jnz     bad_guess
  542.         mov word ptr cs:PatchSpot[1], offset szString1
  543. bad_guess:
  544.         call Reply
  545.         ret
  546. Compare            endp
  547. EndPatch:
  548.         db 0CCh, 0CCh                  ;Identification Mark: END
  549.  
  550. This kind of program is very hard to debug. For testing, I substituted 'xor
  551. al, [si]' first with 'xor al, 00h', which would cause no encryption and is
  552. useful for testing code for final bugs, and then with 'xor al, EBh', which
  553. allowed me to verify that the correct bytes were being encrypted (it never
  554. hurts to check, after all).
  555.  
  556.  
  557. Episode 4: Summation
  558. --------------------
  559. That should demonstrate the basics of self-modifying code. There are a few
  560. techniques to consider to make development easier, though really any SMC
  561. programs will be tricky.
  562.  
  563. The most important thing is to get your program running completely before you
  564. start overwriting any of its code segments. Next, always create a program that
  565. performs the reverse of any decryption/encryption code--not only does this
  566. speed up comilation and testing by automating the encryption of code areas
  567. that will be decrypted at runtime, it also provides a good tool for error
  568. checking using a disassembler (i.e. encrypt the code, disassemble, decrypt the
  569. code, disassemble, compare). In fact, it is a good idea to encapsulate the SMC
  570. portion of your program in a separate executable and test it on the compiled
  571. "release product" until all of the bugs are out of the decryption routine, and
  572. only then add the decryption routine to your final code. The CCh 'landmarks'
  573. (codemarks?) are extremely useful as well.
  574.  
  575. Finally, do your debugging with debug.com for DOS applications--the debugger
  576. is quick, small, and if it crashes you simply lose a Windows DOS box. The
  577. ability to view the program address space after the program has terminated but
  578. before it is unloaded is another distinct advantage.
  579.  
  580. More complex examples of SMC programs can be found in Dark Angel's code, the
  581. Rhince engine, or in any of the permutation engines used in ploymorphic
  582. viruses. Acknowledgements go to Sun-Tzu for the stack technique used in his
  583. ghf-crackme program.
  584.  
  585.  
  586. ::/ \::::::.
  587. :/___\:::::::.
  588. /|    \::::::::.
  589. :|   _/\:::::::::.
  590. :| _|\  \::::::::::.
  591. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  592.                                                       Going Ring0 in Windows 9x
  593.                                                       by Halvar Flake
  594.  
  595.  
  596. This article gives a short overview over two ways to go Ring0 in Windows 9x in
  597. an undocumented way, exploiting the fact that none of the important system
  598. tables in Win9x are on pages which are protected from low-privilege access.
  599.  
  600. A basic knowledge of Protected Mode and OS Internals are required, refer to
  601. your Assembly Book for that :-) The techniques presented here are in no way a
  602. good/clean way to get to a higher privilege level, but since they require only
  603. a minimal coding effort, they are sometimes more desirable to implement than a
  604. full-fledged VxD.
  605.  
  606.  1. Introduction
  607.  ---------------
  608. Under all modern Operating Systems, the CPU runs in protected mode, taking
  609. advantage of the special features of this mode to implementvirtual memory,
  610. multitasking etc. To manage access to system-critical resources (and to thus
  611. provide stability) a OS is in need of privilege levels, so that a program can't
  612. just switch out of protected mode etc. These privilege levels are represented
  613. on the x86 (I refer to x86 meaning 386 and following) CPU by 'Rings', with
  614. Ring0 being the most privileged and Ring3 being the least privileged level.
  615. Theoretically, the x86 is capable of 4 privilege levels, but Win32 uses only
  616. two of them, Ring0 as 'Kernel Mode' and Ring3 as 'User Mode'.
  617.  
  618. Since Ring0 is not needed by 99% of all applications, the only documented way
  619. to use Ring0 routines in Win9x is through VxDs. But VxDs, while being the only
  620. stable and recommended way, are work to write and big, so in a couple of
  621. specialized situations, other ways to go Ring0 are useful.
  622.  
  623. The CPU itself handles privilege level transitions in two ways: Through
  624. Exceptions/Interrupts and through Callgates. Callgates can be put in the LDT or
  625.  GDT, Interrupt-Gates are found in the IDT.
  626.  
  627. We'll take advantage of the fact that these tables can be freely written to
  628. from Ring3 in Win9x (NOT IN NT !).
  629.  
  630.  
  631. 2. The IDT method
  632. -----------------
  633. If an exception occurs (or is triggered), the CPU looks in the IDT to the
  634. corresponding descriptor. This descriptor gives the CPU an Address and Segment
  635. to transfer control to. An Interrupt Gate descriptor looks like this:
  636.  
  637.      --------------------------------- ---------------------------------
  638.                                           D D
  639.            1.Offset (16-31)             P P P 0 1 1 1 0 0 0 0 R R R R R   +4
  640.                                           L L
  641.      --------------------------------- ---------------------------------
  642.            2.Segment Selector               3.Offset (0-15)                0
  643.      --------------------------------- ---------------------------------
  644.           DPL == Two bits containing the Descriptor Privilege Level
  645.           P   == Present bit
  646.           R   == Reserved bits
  647.  
  648. The first word (Nr.3) contains the lower word of the 32-bit address of the
  649. Exception Handler. The word at +6 contains the high-order word. The word at +2
  650. is the selector of the segment in which the handler resides.
  651.  
  652. The word at +4 identifies the descriptor as Interrupt Gate, contains its
  653. privilege and the present bit. Now, to use the IDT to go Ring0, we'll create a
  654. new Interrupt Gate which points to our Ring0 procedure, save an old one and
  655. replace it with ours.
  656.  
  657. Then we'll trigger that exception. Instead of passing control to Window's own
  658. handler, the CPU will now execute our Ring0 code. As soon as we're done, we'll
  659. restore the old Interrupt Gate.
  660.  
  661. In Win9x, the selector 0028h always points to a Ring0-Code Segment, which spans
  662. the entire 4 GB address range. We'll use this as our Segment selector.
  663.  
  664. The DPL has to be 3, as we're calling from Ring3, and the present bit must be
  665. set. So the word at +4 will be 1110111000000000b => EE00h. These values can
  666. be hardcoded into our program, we have to just add the offset of our Ring0
  667. Procedure to the descriptor. As exception, you should preferrably use one that
  668. rarely occurs, so do not use int 14h ;-)
  669.  
  670. I'll use int 9h, since it is (to my knowledge) not used on 486+.
  671.  
  672. Example code follows (to be compiled with TASM 5):
  673.  
  674. -------------------------------- bite here -----------------------------------
  675.  
  676. .386P
  677. LOCALS
  678. JUMPS
  679. .MODEL FLAT, STDCALL
  680.  
  681. EXTRN ExitProcess : PROC
  682.  
  683. .data
  684.  
  685. IDTR        df 0            ; This will receive the contents of the IDTR
  686.                             ; register
  687.  
  688. SavedGate   dq 0            ; We save the gate we replace in here
  689.  
  690. OurGate     dw 0            ; Offset low-order word
  691.             dw 028h         ; Segment selector
  692.             dw 0EE00h       ;
  693.             dw 0            ; Offset high-order word
  694.  
  695.  
  696.  
  697. .code
  698.  
  699. Start:
  700.       mov      eax, offset Ring0Proc
  701.       mov      [OurGate], ax              ; Put the offset words
  702.       shr      eax, 16                    ; into our descriptor
  703.       mov      [OurGate+6], ax
  704.  
  705.       sidt     fword ptr IDTR
  706.       mov      ebx, dword ptr [IDTR+2]    ; load IDT Base Address
  707.       add      ebx, 8*9                   ; Address of int9 descriptor in ebx
  708.  
  709.       mov      edi, offset SavedGate
  710.       mov      esi, ebx
  711.       movsd                               ; Save the old descriptor
  712.       movsd                               ; into SavedGate
  713.  
  714.       mov      edi, ebx
  715.       mov      esi, offset OurGate
  716.       movsd                               ; Replace the old handler
  717.       movsd                               ; with our new one
  718.  
  719.       int      9h                         ; Trigger the exception, thus
  720.                                           ; passing control to our Ring0
  721.                                           ; procedure
  722.  
  723.       mov      edi, ebx
  724.       mov      esi, offset SavedGate
  725.       movsd                               ; Restore the old handler
  726.       movsd
  727.  
  728.       call     ExitProcess, LARGE -1
  729.  
  730. Ring0Proc PROC
  731.       mov      eax, CR0
  732.       iretd
  733. Ring0Proc ENDP
  734.  
  735. end Start
  736.  
  737. -------------------------------- bite here -----------------------------------
  738.  
  739.  
  740. 3. The LDT Method
  741. -----------------
  742. Another possibility of executing Ring0-Code is to install a so- called callgate
  743. in either the GDT or LDT. Under Win9x it is a little bit easier to use the LDT,
  744. since the first 16 descriptors in it are always empty, so I will only give
  745. source for that method here.
  746.  
  747. A Callgate is similar to a Interrupt Gate and is used in order to transfer
  748. control from a low-privileged segment to a high-privileged segment using a CALL
  749. instruction.
  750.  
  751. The format of a callgate is:
  752.  
  753.      --------------------------------- ---------------------------------
  754.                                           D D                   D D D D
  755.            1.Offset (16-31)             P P P 0 1 1 0 0 0 0 0 0 W W W W   +4
  756.                                           L L                   C C C C
  757.      --------------------------------- ---------------------------------
  758.            2.Segment Selector               3.Offset (0-15)                0
  759.      --------------------------------- ---------------------------------
  760.           P   == Present bit
  761.           DPL == Descriptor Privilege Level
  762.           DWC == Dword Count, number of arguments copied to the ring0 stack
  763.  
  764. So all we have to do is to create such a callgate, write it into one of the
  765. first 16 descriptors, then do a far call to that descriptor to execute our
  766. Ring0 code.
  767.  
  768. Example Code:
  769.  
  770. -------------------------------- bite here -----------------------------------
  771.  
  772. .386P
  773. LOCALS
  774. JUMPS
  775. .MODEL FLAT, STDCALL
  776.  
  777. EXTRN ExitProcess : PROC
  778.  
  779. .data
  780.  
  781. GDTR        df 0            ; This will receive the contents of the IDTR
  782.                             ; register
  783.  
  784. CallPtr     dd 00h          ; As we're using the first descriptor (8) and
  785.             dw 0Fh          ; its located in the LDT and the privilege level
  786.                             ; is 3, our selector will be 000Fh.
  787.                             ; That is because the low-order two bits of the
  788.                             ; selector are the privilege level, and the 3rd
  789.                             ; bit is set if the selector is in the LDT.
  790.  
  791. OurGate     dw 0            ; Offset low-order word
  792.             dw 028h         ; Segment selector
  793.             dw 0EC00h       ;
  794.             dw 0            ; Offset high-order word
  795.  
  796. .code
  797.  
  798. Start:
  799.       mov      eax, offset Ring0Proc
  800.       mov      [OurGate], ax              ; Put the offset words
  801.       shr      eax, 16                    ; into our descriptor
  802.       mov      [OurGate+6], ax
  803.  
  804.       xor      eax, eax
  805.  
  806.       sgdt     fword ptr GDTR
  807.       mov      ebx, dword ptr [GDTR+2]    ; load GDT Base Address
  808.       sldt     ax
  809.       add      ebx, eax                   ; Address of the LDT descriptor in
  810.                                           ; ebx
  811.       mov      al, [ebx+4]                ; Load the base address
  812.       mov      ah, [ebx+7]                ; of the LDT itself into
  813.       shl      eax, 16                    ; eax, refer to your pmode
  814.       mov      ax, [ebx+2]                ; manual for details
  815.  
  816.       add      eax, 8                     ; Skip NULL Descriptor
  817.  
  818.       mov      edi, eax
  819.       mov      esi, offset OurGate
  820.       movsd                               ; Move our custom callgate
  821.       movsd                               ; into the LDT
  822.  
  823.       call     fword ptr [CallPtr]        ; Execute the Ring0 Procedure
  824.  
  825.       xor      eax, eax                   ; Clean up the LDT
  826.       sub      edi, 8
  827.       stosd
  828.       stosd
  829.  
  830.       call     ExitProcess, LARGE -1
  831.  
  832. Ring0Proc PROC
  833.       mov      eax, CR0
  834.       retf
  835. Ring0Proc ENDP
  836.  
  837. end Start
  838.  
  839. -------------------------------- bite here -----------------------------------
  840.  
  841. Well, that's all for now folks. This method can be easily changedto use the GDT
  842. instead which would save a few bytes in case you have to optimize heavily.
  843.  
  844. Anyways, do use these methods with care, they will NOT run on NT and are
  845. generally not exactly a clean or stable way to do these things.
  846.  
  847.  
  848. Credits & Thanks
  849. ----------------
  850. The IDT-Method taken from the CIH virus & Stone's example source at
  851. http://www.cracking.net.
  852. The LDT-Method was done by me, but without IceMans & The_Owls help I would
  853. still be stuck, so all credits go to them.
  854.  
  855.  
  856. ::/ \::::::.
  857. :/___\:::::::.
  858. /|    \::::::::.
  859. :|   _/\:::::::::.
  860. :| _|\  \::::::::::.
  861. :::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING
  862.                                                      Win32 ASM: The Basics
  863.                                                      by Iczelion
  864.  
  865.  
  866. The required tools:
  867.         -Microsoft Macro Assembler 6.1x : MASM support of Win32 programming
  868.           starts from version 6.1. The latest version is 6.13 which
  869.           is a patch to previous version of 6.11. Win98 DDK includes MASM
  870.           6.11d which you can download from Microsoft at
  871.           http://www.microsoft.com/hwdev/ddk/download/win98ddk.exe
  872.           But be warned, this monstrosity is huge, 18.5 MB in size. MASM 6.13
  873.           patch can also be downloaded from
  874.           ftp://ftp.microsoft.com/softlib/mslfiles/ml613.exe
  875.         -Microsoft import libraries : You can use the import libraries from
  876.           Visual C++. Some are included in Win98 DDK.
  877.         -Win32 API Reference : You can download it from Borland's site:
  878.          ftp://ftp.borland.com/pub/delphi/techpubs/delphi2/win32.zip
  879.  
  880. Here's a brief description of the assembly process.
  881.  
  882. MASM 6.1x comes with two essential tools: ml.exe and link.exe. ml.exe is the
  883. assembler. It takes in the assembly source code (.asm) and produces an object
  884. file (.obj) . An object file is an intermediate file between the source code
  885. and the executable file. It needs some address fixups which are the services
  886. provided by link.exe. Link.exe makes an object file into an executable file by
  887. several means such as adding the codes from other modules to the object files
  888. or providing the address fixups, addingr esouces, etc.
  889.  
  890. For example:
  891.         ml skeleton.asm    ---> this produces skeleton.obj
  892.         link skeleton.obj  ---> this produces skeleton.exe
  893.  
  894. The above lines are simplification of course. In the real world, you must add
  895. several switches to ml.exe and link.exe to customize your application. Also
  896. there will be several files you must link with the object file in order to
  897. create your application.
  898.  
  899. Win32 programs run in protected mode which is available since 80286. But 80286
  900. is now history. So we only have to concern ourselves with 80386 and its
  901. descendants. Windows run each Win32 program in separated virtual space. That
  902. means each Win32 program will have its own 4 GB address space. Each program is
  903. alone in its address space. This is in contrast to the situation in Win16. All
  904. Win16 programs can *see* each other. Not so in Win32. This feature helps reduce
  905. the chance of one program writing over other program's code/data.
  906.  
  907. Memory model is also drastically different from the old days of the 16-bit
  908. world. Under Win32, we need not be concerned with memory model or segment
  909. anymore! There's only one memory model: Flat memory model. There's no more 64K
  910. segments. The memory is a  large continuous space of 4 GB. That also means you
  911. don't have to play with segment registers. You can use any segment register to
  912. address any point in the memory space. That's a GREAT help to programmers. This
  913. is what makes Win32 assembly programming as easy as C.
  914.  
  915. We will examine a miminal skeleton of a Win32 assembly program. We'll add more
  916. flesh to it later. Here's the skeleton program. If you don't understand some of
  917. the codes, don't panic. I'll explain each of them later.
  918.  
  919. .386
  920. .MODEL Flat, STDCALL
  921. .DATA
  922.     <Your initialized data>
  923.     ......
  924. .DATA?
  925.    <Your uninitialized data>
  926.    ......
  927. .CONST
  928.    <Your constants>
  929.    ......
  930. .CODE
  931.    <label>
  932.     <Your code>
  933.    .....
  934.     end <label>
  935. That's all! Let's analyze this skeleton program.
  936.  
  937. .386
  938. This is an assembler directive, telling the assembler to use 80386 instruction
  939. set. You can also use .486, .586 but the safest bet is to stick to .386.
  940.  
  941. .MODEL FLAT, STDCALL
  942. .MODEL is an assembler directive that specifies memory model of your program.
  943. Under Win32, there's only on model, FLAT model. STDCALL tells MASM about
  944. parameter passing convention. Parameter passing convention specifies the order
  945. of  parameter passing, left-to-right or right-to-left, and also who will
  946. balance the stack frame after the function call.
  947.  
  948. Under Win16, there are two types of calling convention, C and PASCAL C calling
  949. convention passes parameters to the function from right to left, that is , the
  950. rightmost parameter is pushed on the stack first. The caller is responsible for
  951. balancing the stack frame after the call. For example, in order to call a
  952. function named foo(int first_param, int second_param, int third_param) in C
  953. calling convention the asm codes will look like this:
  954.  
  955.      push  [third_param]               ; Push the third parameter
  956.      push  [second_param]              ; Followed by the second
  957.      push  [first_param]               ; And the first
  958.      call    foo
  959.      add    sp, 12                     ; The caller balances the stack frame
  960.  
  961. PASCAL calling convention is the reverse of C calling convention. It pushes
  962. parameters on the stack from left to right and the callee is responsible for
  963. the stack balancing after the call.
  964.  
  965. Win16 adopts PASCAL convention because it produces smaller codes. C convention
  966. is useful when you don't know how many parameters will be passed to the
  967. function as in the case of wsprintf(). In the case of wsprintf(), the function
  968. has no way to determine beforehand how many parameters will be pushed on the
  969. stack, so it cannot balance the stack correctly. The caller is the one who
  970. knows how many bytes are pushed on the stack so it's right and proper that it's
  971. also the one who balances the stack frame after the call.
  972.  
  973. STDCALL is the hybrid of C and PASCAL convention. It pushes parameters on the
  974. stack from right to left but the callee is responsible for stack balancing
  975. after the call. Win32 platform use STDCALL exclusively. Except in one case:
  976. wsprintf(). You must use C calling convention with wsprintf().
  977.  
  978. .DATA
  979. .DATA?
  980. .CONST
  981. .CODE
  982. All four directives are what are called sections. You don't have segments in
  983. Win32 anymore, remember? But you can divide your entire address space into
  984. logical sections. The start of one section denotes the end of the previous
  985. section. There are two groups of section: data and code. Data sections are
  986. divided into 3 categories:
  987.  
  988.    * .DATA    This section contains initialized data of your program.
  989.    * .DATA?  This section contains uninitialized data of your program.
  990.      Sometimes you just want to preallocate some memory but doesn't want to
  991.      initialize it. This section exists for that purpose.
  992.    * .CONST  This section contains declaration of constants used by your
  993.      program. Constants in this section can never be modified in your
  994.      program. They are just *constant*.
  995.  
  996. You don't have to use all three sections in your program. Declare only the
  997. section(s) you want to use.
  998.  
  999. There's only one section for code: .CODE. This is where your codes reside.
  1000. Example:
  1001.  
  1002. <label>
  1003. end <label>
  1004.  
  1005. ...where <label> is any arbitrary label is used to specify the extent of your
  1006. code. Both labels must be identical.  All your codes must reside between
  1007. <label> and end <label>
  1008.  
  1009.  
  1010. ::/ \::::::.
  1011. :/___\:::::::.
  1012. /|    \::::::::.
  1013. :|   _/\:::::::::.
  1014. :| _|\  \::::::::::.
  1015. :::\_____\:::::::::::................................WIN32.ASSEMBLY.PROGRAMMING
  1016.                                                      MessageBox Display
  1017.                                                      by Iczelion
  1018.  
  1019.  
  1020. We will create a fully functional Windows program that displays a message box
  1021. saying "Win32 assembly is great!".
  1022.  
  1023. Windows prepares a wealth of resources for use by Windows programs. Central to
  1024. this is the Windows API (Application Programming Interface). Windows API is a
  1025. huge collection of very useful functions that resides in Windows itself, ready
  1026. to be used by any Windows programs.
  1027.  
  1028. These functions are stored in several dynamic-linked libraries (DLLs) such as
  1029. kernel32.dll, user32.dll and gdi32.dll, to name a few. Kernel32.dll contains
  1030. API functions that deal with memory and process management. User32.dll controls
  1031. the user interface aspects of your programs. Gdi32.dll is responsible for
  1032. graphics operation. Other than "the main three", there are other DLLs that your
  1033. program can make use of, provided you have enough information about the desired
  1034. API functions stored in them.
  1035.  
  1036. Windows programs dynamically link to these DLLs, i.e. the codes of API
  1037. functions are not included in the executable file. This is very different from
  1038. what's called static linking in which actual codes from software libraries are
  1039. included in the executable files. In order for programs to know where to find
  1040. the desired API functions at runtime, enough information must be embedded into
  1041. the executable file for it to be able to select the correct DLLs and correct
  1042. functions. That information is in import libraries. You must link your
  1043. programs with the correct import libraries or it will not be able to locate
  1044. the desired API functions.
  1045.  
  1046. There are two types of API functions: One for ANSI and the other for Unicode.
  1047. The name of API functions for ANSI are postfixed with "A", eg. MessageBoxA.
  1048. Those for Unicode are postfixed with "W" (for Wide Char, I think).
  1049.  
  1050. Windows 95 natively supports ANSI and Windows NT Unicode. But most of the time,
  1051. you will use an include file which can determine and select the appropriate API
  1052. functions for your platform. Just refer to the API function name without the
  1053. postfix.
  1054.  
  1055. I'll present the bare program skeleton below. We will fill it out later.
  1056.  
  1057. .386
  1058. .model flat, stdcall
  1059. .data
  1060. .code
  1061.     Main:
  1062.     end Main
  1063.  
  1064. Every Windows program must call an API function, ExitProcess, when it wants to
  1065. quit to Windows. In this respect, ExitProcess is equivalent to int 21h, ah=4Ch
  1066. in DOS.
  1067.  
  1068. Here's the function prototype of ExitProcess from winbase.h:
  1069.  
  1070. void WINAPI ExitProcess(UINT uExitCode);
  1071.  
  1072. -void means the function does not return any value to the caller.
  1073. -WINAPI is an alias of STDCALL calling convention.
  1074. -UINT is a data type, "unsigned integer", which is a 32-bit value under Win32
  1075. (it's a 16-bit value under Win16)
  1076. -uExitCode is the 32-bit return code to Windows. This value is not used by
  1077. Windows as of now.
  1078.  
  1079. In order to call ExitProcess from an assembly program, you must first declare
  1080. the function prototype for ExitProcess.
  1081.  
  1082. .386
  1083. .model flat, stdcall
  1084.  ExitProcess     PROTO  :DWORD
  1085. .data
  1086. .code
  1087. Main:
  1088.     invoke    ExitProcess, 0
  1089. end Main
  1090.  
  1091. That's it. Your first working Win32 program. Save it under the name msgbox.asm.
  1092. Assuming ml.exe is in your path, assemble msgbox.asm with:
  1093.  
  1094.      ml  /c  /coff  /Cp msgbox.asm
  1095.  
  1096. /c tells MASM to assemble the source file into an object file only. Do not
  1097.    invoke Link.exe automatically.
  1098. /coff tells MASM to create .obj file in COFF format.
  1099. /Cp tells MASM to preserve case of user identifiers
  1100.  
  1101. Then go on with link:
  1102.  
  1103.      link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm\lib  msgbox.obj
  1104.      kernel32.lib
  1105.  
  1106. /SUBSYSTEM:WINDOWS  informs Link.exe on which platform the executable is
  1107.       intended to run
  1108. /LIBPATH:<path to import library> tells Link where the import libraries
  1109.       are. In my PC, they're located in c:\masm\lib.
  1110.  
  1111. Now that you get msgbox.exe. Go on, run it. You'll find that it does nothing.
  1112. Well, we haven't put anything interesting in it yet. But it's a Windows
  1113. program nonetheless. And look at its size! In my PC, it is 1,536 bytes.
  1114. The line:
  1115.  
  1116.      ExitProcess     PROTO     :DWORD
  1117.  
  1118. is a function prototype. You create one by declaring the function name followed
  1119. by the keyword "PROTO" and lists of data types of the parameters prefixed by
  1120. colons. MASM uses function prototypes to type checking which will prevent nasty
  1121. stack errors that may pass unnoticed otherwise.
  1122.  
  1123. The best place for function prototypes is in an include file. You can create an
  1124. include file full of frequently used function prototypes and data structures
  1125. and include it at the beginning of your asm source code.
  1126.  
  1127. You call the API function by using "invoke" keyword:
  1128.  
  1129.           invoke  ExitProcess, 0
  1130.  
  1131. INVOKE is really a kind of high-level call. It checks number and types of
  1132. parameters and pushes parameters on the stack according to the specified
  1133. calling convention (in this case, stdcall). By using INVOKE instead of a normal
  1134. call, you can prevent stack errors from incorrect parameter passing. Very
  1135. useful. The syntax is:
  1136.  
  1137.           INVOKE  expression [,arguments]
  1138.  
  1139. where expression is a label or function name.
  1140.  
  1141. Next we're going to put a message box in our program. Its function declaration
  1142. is:
  1143.  
  1144. int WINAPI MessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpCaption, UINT
  1145. uType);
  1146.  
  1147. -hwnd is the handle to parent window
  1148. -lpText is a pointer to the text you want to display in the client area of the
  1149. message box
  1150. -lpCaption is a pointer to the caption of the message box
  1151. -uType specifies the icon and the number and type of buttons on the message
  1152. box
  1153.  
  1154. Under Win32 , HWND, LPCSTR, and UINT are all 32 bits in size.
  1155.  
  1156. Let's modify msgbox.asm to include the message box.
  1157.  
  1158. .386
  1159. .model flat, stdcall
  1160. ExitProcess      PROTO      :DWORD
  1161. MessageBoxA PROTO      :DWORD, :DWORD, :DWORD, :DWORD
  1162. .data
  1163. MsgBoxCaption  db "Our First Program",0
  1164. MsgBoxText     db "Win32 Assembly is Great!",0
  1165. .const
  1166. NULL        equ  0
  1167. MB_OK       equ  0
  1168. .code
  1169. Main:
  1170.      INVOKE    MessageBoxA, NULL, ADDR MsgBoxText, ADDR MsgBoxCaption, MB_OK
  1171.      INVOKE    ExitProcess, NULL
  1172. end Main
  1173.  
  1174. Assemble it by:
  1175.         ml /c /coff /Cp msgbox.asm
  1176.         link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib msgbox kernl32.lib
  1177. user32.lib
  1178.  
  1179. You have to include user32.lib in your Link parameter, since link info of
  1180. MessageBoxA is in user32.lib.
  1181.  
  1182. You'll see a message box displaying the text "Win32 Assembly is Great!". Let's
  1183. look again at the source code:
  1184.  
  1185. We define two zero-terminated strings in .data section. Remember that all
  1186. strings in Windows must be terminated with zero (ASCIIZ).
  1187.  
  1188. We define two constants in .const section. We use constants to improve the
  1189. clarity of the source code.
  1190.  
  1191. Look at the parameters of MessageBoxA. The first parameter is NULL. This
  1192. means that there's no window that *owns* this message box.
  1193.  
  1194. The operator "ADDR" is used to pass the address of the label to the function.
  1195. This operator is specific to MASM. No TASM-equivalent exists. It functions like
  1196. "OFFSET" operator but with some differences:
  1197.         1. It doesn't accept forward reference. If you want to use "ADDR foo",
  1198.            you have to declare "foo" before using ADDR operator.
  1199.         2. It can be used with a local variable. A local variable is the
  1200.            variable that is created on the stack. OFFSET operator cannot be
  1201.            used in this situation because the assembler doesn't know the true
  1202.            address of the local variable at assemble time.
  1203.  
  1204.  
  1205. ::/ \::::::.
  1206. :/___\:::::::.
  1207. /|    \::::::::.
  1208. :|   _/\:::::::::.
  1209. :| _|\  \::::::::::.
  1210. :::\_____\:::::::::::........................THE.C.STANDARD.LIBRARY.IN.ASSEMBLY
  1211.                                          The _itoa, _ltoa and _ultoa functions
  1212.                                          by Xbios2
  1213.  
  1214.  
  1215. ATTENTION I:
  1216. This is based on Borland's C++ 4.02. Whenever possible I've checked it with any
  1217. other library / program containing the specific functions, but differences may
  1218. exist between this and your version of C. Also this is strictly 32-bit code,
  1219. Windows compiler. No DOS or UNIX.]
  1220.  
  1221. ATTENTION II:
  1222. Size comparisons are extremely easy to do. Speed comparison's aren't. The diff-
  1223. erences in speed I give are based on RDTSC timings, but they DON'T take into
  1224. account extreme cases. That's why I don't give exact clock cycles. Of course if
  1225. you need exact clock cycles for your Pentium II, you can always buy me one :)
  1226.  
  1227.  
  1228. The C language offers three functions to convert an integer to ASCII:
  1229.  
  1230. char *itoa(int value, char *string, int radix);
  1231. char *ltoa(long value, char *string, int radix);
  1232. char *ultoa(unsigned long value, char *string, int radix);
  1233.  
  1234. _itoa and _ltoa do _exactly_ the same thing. This is because an integer _is_ a
  1235. long in 32-bit code. Yet they are different: _itoa has some _completely_
  1236. useless code in it (in 16bit this code would sign-extend value if radix=10).
  1237. Yet the result is always the same, so _ltoa from here on means both _ltoa and
  1238. _itoa. _ultoa is exactly the same as _ltoa and _itoa, except when radix=10 and
  1239. value < 0.
  1240.  
  1241. Anyway all these functions call this function:
  1242.  
  1243. ___longtoa(value, *string, radix, signed, char10)
  1244.  
  1245. The first three parameters are passed 'as is', signed is set to 1 by _ltoa if
  1246. radix=10 else it is set to 0 and char10 is the character that corresponds to 10
  1247. if radix>10, and is always set to 'a' (___longtoa is also used by printf, which
  1248. has an option to have uppercase chars in Hex).
  1249.  
  1250. ___longtoa does the following (and it does it with badly written code):
  1251.  
  1252. 1. Checks that 2<=radix<=36, if it isn't returns '0'
  1253. 2. If signed=1 and value<0 add a '-' to the string and neg the value
  1254. 3. Loop1: create a pseudo-string in the stack, reversed
  1255. 4. Loop2: convert and copy the pseudo-string into string
  1256.  
  1257. The check on radix is necessary because:
  1258. radix=0 would generate an INT0 (divide by zero)
  1259. radix=1 would put the program in an infinite loop, destroying the stack
  1260. radix=37 for value=36 would return '}', the character after 'z'
  1261.  
  1262. The two loops are necessary because of the way the conversion is done (see code
  1263. later). To implement a single-loop conversion, the number of digits should be
  1264. calculated in advance, which results in less efficient code (the number of
  1265. digits in value is n=(int)(log(value)/log(radix))+1, but using one more loop is
  1266. much faster).
  1267.  
  1268. Including the disassembly of C's functions would create a really large article,
  1269. and anyway they're just examples of really bad code. So straight to the result:
  1270.  
  1271. ltoa    proc
  1272.     cmp    dword ptr [esp+0Ch], 10
  1273.     sete    ch
  1274.     mov    cl, 'a'-'0'-10
  1275.     jmp    short longtoa
  1276.  
  1277. ultoa:
  1278.     mov    cx, 'a'-'0'-10
  1279.  
  1280. longtoa:
  1281.     push    ebx
  1282.     push    edi
  1283.     push    esi
  1284.     sub    esp, 24h
  1285.     mov    ebx, [esp+3Ch]        ; radix
  1286.     mov    eax, [esp+34h]        ; value
  1287.     mov    edi, [esp+38h]        ; string
  1288.     cmp    ebx, 2
  1289.     jl    short _ret
  1290.     cmp    ebx, 36
  1291.     jg    short _ret
  1292.     or    eax, eax
  1293.     jge    short skip
  1294.     cmp    byte ptr ch, 0        ; _ltoa ?
  1295.     jz    short skip
  1296.     mov    byte ptr [edi],    '-'
  1297.     inc    edi
  1298.     neg    eax
  1299. skip:    mov    esi, esp
  1300.  
  1301. loop1:    xor    edx, edx
  1302.     div    ebx
  1303.     mov    [esi], dl
  1304.     inc    esi
  1305.     or    eax, eax
  1306.     jnz    loop1
  1307.  
  1308. loop2:    dec    esi
  1309.     mov    al, [esi]
  1310.     cmp    al, 10
  1311.     jl    short nochar
  1312.     add    al, cl
  1313. nochar:    add    al, '0'
  1314.     stosb
  1315.     cmp    esi, esp
  1316.     jg    short loop2
  1317.  
  1318. _ret:    mov    byte ptr [edi],    0
  1319.     mov    eax, [esp+38h]
  1320.     add    esp, 24h
  1321.     pop    esi
  1322.     pop    edi
  1323.     pop    ebx
  1324.     ret
  1325. ltoa    endp
  1326.  
  1327. This is a 3 into 1 procedure. ltoa and ultoa take the same parameters as the
  1328. standard C functions. longtoa was changed to take from the stack the same
  1329. parameters as ltoa and ultoa, while signed and char10 are passed in CH and CL
  1330. respectively. This way ltoa and ultoa 'see' longtoa as 'their' code, not as a
  1331. different procedure (this is to avoid a common problem in C, procedures that
  1332. just 'forward' their parameters to another function).
  1333.  
  1334. This code compiles to 102 bytes (and it could be optimized to gain some more
  1335. bytes) whereas the standard C code takes 270 bytes. Specifically:
  1336.  
  1337. function   C size     Asm size
  1338. ------------------------------
  1339. itoa          60           0
  1340. ltoa          40          12
  1341. ultoa         27           4
  1342. longtoa      143          86
  1343.             ------      ------
  1344.      total   270         102
  1345.  
  1346. It also runs 2x faster than ltoa. And of course, this is a fully C-compatible
  1347. version of ltoa and ultoa. Of course it can be changed from C-compatible to
  1348. suit specific needs (e.g make it stdcall instead of cdecl, or if speed and size
  1349. are needed remove the check for the radix, and so on...)
  1350.  
  1351. Anyway, it is rather strange that you'll ever use values of radix other than 2,
  1352. 8, 10 or 16. So if speed or size is of essence, a better, more specific routine
  1353. can be written. For example, consider this routine which stores the value of
  1354. EAX as a binary number at the address specified by EDI:
  1355.  
  1356. ultob    proc
  1357.     mov    ecx, 32
  1358. more1:    shl    eax, 1
  1359.     dec    ecx
  1360.     jc    more2
  1361.     jnl    more1
  1362. more2:    setc    dl
  1363.     add    dl, '0'
  1364.     shl    eax, 1
  1365.         mov     [edi], dl
  1366.         inc     edi
  1367.         dec     ecx
  1368.     jnl    more2
  1369.     mov    [edi], al
  1370.     ret
  1371. ultob    endp
  1372.  
  1373. This runs 14x faster than C ltoa, and 7x faster than Asm ltoa, and is only 29
  1374. bytes long. But this article is long enough, so wait for another article on
  1375. specific 'ltoa' functions (who knows, maybe if I decide to write a 'printf'
  1376. function in Asm, which would use them...).
  1377.  
  1378.  
  1379. ::/ \::::::.
  1380. :/___\:::::::.
  1381. /|    \::::::::.
  1382. :|   _/\:::::::::.
  1383. :| _|\  \::::::::::.
  1384. :::\_____\:::::::::::............................................THE.UNIX.WORLD
  1385.                                                   x86 ASM Programming for Linux
  1386.                                                   by mammon_
  1387.  
  1388.  
  1389. Essentially this article is an excuse to combine two of my favorite coding
  1390. interests: the Linux operating system and assembly language programming. Both
  1391. of these need (or should need) no introduction; like Win32 assembly, Linux
  1392. assembly runs in 32-bit protected mode...however it has the distinct advantage
  1393. of allowing you to call the C standard library functions as well as any of the
  1394. usual Linux "shared" library functions. I have begun with a brief introduction
  1395. on compiling assembly language programs in Linux; for greater readability you
  1396. may want to skip over this to the "Basics" section.
  1397.  
  1398.  
  1399. Compiling And Linking
  1400. ---------------------
  1401. The two main assemblers for Linux are Nasm, the (free) Netwide Assembler, and
  1402. GAS, the (also free) Gnu Assembler which is integrated into GCC. I will focus
  1403. on Nasm in this article and leave GAS for a later date, as it uses the AT&T
  1404. syntax and thus would require a lengthy introduction.
  1405.  
  1406. Nasm should be invoked with the ELF format option ("nasm -f elf hello.asm");
  1407. the resulting object is linked with GCC ("gcc hello.o") to produce the final
  1408. ELF binary. The following script can be used to compile ASM modules; I wrote
  1409. it to be very simple, so all it does is take the first filename passed to it
  1410. (I recommend naming it with a ".asm" extension), compile it with nasm, and
  1411. link it with gcc.
  1412.  
  1413. #!/bin/sh
  1414. # assemble.sh =========================================================
  1415. outfile=${1%%.*}
  1416. tempfile=asmtemp.o
  1417. nasm -o $tempfile -f elf $1
  1418. gcc $tempfile -o $outfile
  1419. rm $tempfile -f
  1420. #EOF ==================================================================
  1421.  
  1422.  
  1423. The Basics
  1424. ----------
  1425. It is best, of course, to start off with an example before launching into the
  1426. OS details. Here is a very basic, "hello-world"-style program:
  1427. ; asmhello.asm ========================================================
  1428. global main
  1429. extern printf
  1430.  
  1431. section .data
  1432. msg    db    "Helloooooo, nurse!",0Dh,0Ah,0
  1433. section .text
  1434. main:
  1435.     push dword msg
  1436.     call printf
  1437.     pop eax
  1438.         ret
  1439. ; EOF =================================================================
  1440. A quick rundown: the "global main" must be declared global--and since we are
  1441. using the GCC linker, the entrypoint must be named "main"--for the OS loader.
  1442. The "extern printf" is simply a declaration for the call later in the program;
  1443. note that this is all that is needed; the parameter sizes do not need to be
  1444. declared. I have sectioned this example into the standard .data and .text
  1445. sections, though this is not strictly necessary--one could get by with only a
  1446. .text segment, just as in DOS.
  1447.  
  1448. In the body of the code, note that you must push the parameters to the call,
  1449. and in Nasm you must declare the size of all ambiguous (i.e. non-register)
  1450. data: hence the "dword" qualifier. Note that just as inother assemblers, Nasm
  1451. assumes that any memory/label reference is intended to mean the address of the
  1452. memory location or label, not its contents. Thus, to specify the address of
  1453. the string 'msg' you would use 'push dword msg', while to specify the contents
  1454. of the string 'msg' you would use 'push dword [msg]' (note this will only
  1455. contain the first 4 bytes of 'msg'). As printf requires a pointer to a string,
  1456. we will specify the address of 'msg'.
  1457.  
  1458. The call to printf is pretty straightforward. Note that you must clean up the
  1459. stack after every call you make (see below); thus, having PUSHed a dword, I
  1460. POP a dword from the stack into a "throwaway" register. Linux programs end
  1461. simply with a RET to the OS, as each process is spawned from the shell (or PID
  1462. 1 ;) and ends by returning control to it.
  1463.  
  1464. Notice that in Linux you use the standard shared libraries that are shipped
  1465. with the OS in lieu of an "API" or Interrupt Services. All external references
  1466. will be taken care of by the GCC linker which takes a lot of the workload off
  1467. the asm coder. Once you get used to the basic quirks, coding assembly in Linux
  1468. is actually easier than on a DOS-based machine!
  1469.  
  1470.  
  1471. The C Calling Syntax
  1472. --------------------
  1473. Linux uses the C calling convention--meaning that arguments are pushed onto the
  1474. stack in reverse order (last arg first), and that the caller must cleanup the
  1475. stack. You can do this either by popping values from the stack:
  1476.      push dword szText
  1477.      call puts
  1478.      pop ecx
  1479. or by directly modifying ESP:
  1480.      push dword szText
  1481.      call puts
  1482.      add esp, 4
  1483.  
  1484. Results from the call are returned in eax or edx:eax if the value is greater
  1485. than 32-bit. EBP, ESI, EDI, and EBX are all saved and restored by the caller.
  1486. Note that you must preserve any other registers you use, as the following will
  1487. illustrate:
  1488. ; loop.asm =================================================================
  1489. global main
  1490. extern printf
  1491. section .text
  1492. msg    db    "HoodooVoodoo WeedooVoodoo",0Dh,0Ah,0
  1493. main:
  1494.    mov ecx, 0Ah
  1495.    push dword msg
  1496. looper:
  1497.    call printf
  1498.    loop looper
  1499.    pop eax
  1500.    ret
  1501. ; EOF ======================================================================
  1502. On first glance this looks pretty simple: since you are going to use the same
  1503. string on the 10 printf() calls, you do not need to clean up the stack. Yet
  1504. when you compile this, the loop never stops. Why? Because somewhere in the
  1505. printf() call ECX is being used and isn't saved. So to make your loop work
  1506. properly you must save the count value in ECX before the call and restoe it
  1507. afterwards, as so:
  1508. ; loop.asm ================================================================
  1509. global main
  1510. extern printf
  1511.  
  1512. section .text
  1513. msg    db    "HoodooVoodoo WeedooVoodoo",0Dh,0Ah,0
  1514. main:
  1515.    mov ecx, 0Ah
  1516. looper:
  1517.    push ecx          ;save Count
  1518.    push dword msg
  1519.    call printf
  1520.    pop eax            ;cleanup stack
  1521.    pop ecx            ;restore Count
  1522.    loop looper
  1523.    ret
  1524. ; EOF ======================================================================
  1525.  
  1526.  
  1527. I/O Port Programming
  1528. --------------------
  1529. But what about direcr hardware access? In Linux you need a kernel-mode driver
  1530. to do anything really tricky...this means your program will end up being two
  1531. parts, one kernel-mode that provides the direct-hardware functionality, the
  1532. other user-mode to provide an interface. The good news is that you can still
  1533. access ports using the IN/OUT commands from a user-mode program.
  1534.  
  1535. To access the I/O ports your program must be granted permission by the OS; to
  1536. do that, you must make an ioperm() call. This function can only be called by a
  1537. user with root access, so you must either setuid() the program to root or run
  1538. the program as root. The ioperm() has the following syntax:
  1539.  
  1540.       ioperm( long StartingPort#, long #Ports, BOOL ToggleOn-Off)
  1541.  
  1542. which means that 'StartingPort#' specifies the first port number to access (0
  1543. is port 0h, 40h is port 40h, etc), '#Ports' specifies how many ports to access
  1544. (i.e., 'StartingPort# = 30h' and '#Ports = 10' would provide access to ports
  1545. 30h-39h), and 'ToggleOn-Off' enables access if TRUE (1) or disables access if
  1546. FALSE (0).
  1547.  
  1548. Once the call to ioperm() is made, the requested ports may be access as
  1549. normal. The program can call ioperm() any number of times and does not need to
  1550. make a subsequent ioperm() call (though the example below does so) as the OS
  1551. will take care of this.
  1552.  
  1553. ; io.asm ====================================================================
  1554. BITS 32
  1555. GLOBAL szHello
  1556. GLOBAL main
  1557. EXTERN printf
  1558. EXTERN ioperm
  1559.  
  1560. SECTION .data
  1561. szText1 db 'Enabling I/O Port Access',0Ah,0Dh,0
  1562. szText2 db 'Disabling I/O Port Acess',0Ah,0Dh,0
  1563. szDone  db 'Done!',0Ah,0Dh,0
  1564. szError db 'Error in ioperm() call!',0Ah,0Dh,0
  1565. szEqual db 'Output/Input bytes are equal.',0Ah,0Dh,0
  1566. szChange db 'Output/Input bytes changed.',0Ah,0Dh,0
  1567.  
  1568. SECTION .text
  1569.  
  1570. main:
  1571.    push dword szText1
  1572.    call printf
  1573.    pop ecx
  1574. enable_IO:
  1575.    push word 1    ; enable mode
  1576.    push dword 04h ; four ports
  1577.    push dword 40h ; start with port 40
  1578.    call ioperm    ; Must be SUID "root" for this call!
  1579.    add ESP, 10    ; cleanup stack (method 1)
  1580.    cmp eax, 0     ; check ioperm() results
  1581.    jne Error
  1582.  
  1583. ;---------------------------------------Port Programming Part--------------
  1584. SetControl:
  1585.    mov al, 96     ; R/W low byte of Counter2, mode 3
  1586.    out 43h, al    ; port 43h = control register
  1587. WritePort:
  1588.    mov bl, 0EEh   ; value to send to speaker timer
  1589.    mov al, bl
  1590.    out 42h, al    ; port 42h = speaker timer
  1591. ReadPort:
  1592.    in al, 42h
  1593.    cmp al, bl     ; byte should have changed--this IS a timer :)
  1594.    jne ByteChanged
  1595. BytesEqual:
  1596.    push dword szEqual
  1597.    call printf
  1598.    pop ecx
  1599.    jmp disable_IO
  1600. ByteChanged:
  1601.    push dword szChange
  1602.    call printf
  1603.    pop ecx
  1604. ;---------------------------------------End Port Programming Part----------
  1605.  
  1606. disable_IO:
  1607.    push dword szText2
  1608.    call printf
  1609.    pop ecx
  1610.    push word 0    ; disable mode
  1611.    push dword 04h ; four ports
  1612.    push dword 40h ; start with port 40h
  1613.    call ioperm
  1614.    pop ecx        ;cleanup stack (method 2)
  1615.    pop ecx
  1616.    pop cx
  1617.    cmp eax, 0     ; check ioperm() results
  1618.    jne Error
  1619.    jmp Exit
  1620. Error:
  1621.    push dword szError
  1622.    call printf
  1623.    pop ecx
  1624. Exit:
  1625.    ret
  1626. ; EOF ======================================================================
  1627.  
  1628.  
  1629. Using Interrupts In Linux
  1630. -------------------------
  1631. Linux is a shared-library environment running in protected mode, meaning there
  1632. are no interrupt services. Right?
  1633.  
  1634. Wrong. I noticed an INT 80 call on some GAS sample source code with the
  1635. comment "sys_write(ebx, ecx, edx)". This function is part of the Linux syscall
  1636. interface, which means that the interrupt 80 must be a gate into the syscall
  1637. services. Poking around in the Linux source code (and ignoring warnings to
  1638. NEVER use the INT 80 interface as the function numbers may be changed at any
  1639. time), I found the "system call numbers" --that is, what function # to pass on
  1640. to INT 80 for each syscall routine-- in the file UNISTD.H. There are 189 of
  1641. them, so I will not list them here...but if you are going to be doing Linux
  1642. assembly, do yourself a favor and print this file out.
  1643.  
  1644. When calling INT 80h, eax must be set to the desired function number. Any
  1645. parameters to the syscall routine must be placed in the following registers in
  1646. order:
  1647.  
  1648.     ebx, ecx, edx, esi, edi
  1649.  
  1650. so that parameter one is placed in ebx, parameter 2 in ecx, etc. Note that
  1651. there is no stack used to pass values to a syscall routine. The result of the
  1652. call will be returned in eax.
  1653.  
  1654. Other than that, the INT 80 interface is the same as regular calls (only a bit
  1655. more fun ;). The following program demonstrates a simple INT 80h call in which
  1656. a program checks and display its own PID. Note the use of printf() format
  1657. string-- it is best to psuedocode this as a C call first, then make the format
  1658. string a DB and to push each variable passed (%s, %d, etc). The C structure
  1659. for this call would be
  1660.  
  1661.      printf( "%d\n", curr_PID);
  1662.  
  1663. Note also that the escape sequences ("\n") are not all that reliable in
  1664. assembly; I had to use the hex values (0Ah,0Dh) for the CR\LF.
  1665.  
  1666. ;pid.asm====================================================================
  1667. BITS 32
  1668. GLOBAL main
  1669. EXTERN printf
  1670.  
  1671. SECTION .data
  1672. szText1 db 'Getting Current Process ID...',0Ah,0Dh,0
  1673. szDone  db 'Done!',0Ah,0Dh,0
  1674. szError db 'Error in int 80!',0Ah,0Dh,0
  1675. szOutput db '%d',0Ah,0Dh,0           ;weird formatting is for printf()
  1676.  
  1677. SECTION .text
  1678. main:
  1679.         push dword szText1    ;opening message
  1680.     call printf
  1681.     pop ecx
  1682. GetPID:
  1683.     mov eax, dword 20     ; getpid() syscall
  1684.         int 80h               ; syscall INT
  1685.         cmp eax, 0            ; there will never be PID 0 ! :)
  1686.     jb Error
  1687.         push eax              ; pass return value to printf
  1688.         push dword szOutput   ; pass format string to printf
  1689.     call printf
  1690.         pop ecx               ; cleanup stack
  1691.     pop ecx
  1692.         push dword szDone     ; ending message
  1693.     call printf
  1694.     pop ecx
  1695.         jmp Exit
  1696. Error:
  1697.         push dword szError
  1698.     call printf
  1699.     pop ecx
  1700. Exit:
  1701.         ret
  1702. ; EOF =====================================================================
  1703.  
  1704.  
  1705. Final Words
  1706. -----------
  1707. Most of the trouble is going to come from getting used to Nasm itself. While
  1708. nasm does come with a man page, it does not by default install it, so you must
  1709. move it (cp or mv) from
  1710. /usr/local/bin/nasm-0.97/nasm.man
  1711. to
  1712. /usr/local/man/man1/nasm.man
  1713. The formatting is a little messed up, but that is easily fixed using the nroff
  1714. directives. It still does not give you the entire Nasm documentation, however;
  1715. for that, copy nasmdoc.txt from
  1716. /usr/local/bin/nasm-0.97/doc/nasmdoc.txt
  1717. to
  1718. /usr/local/man/man1/nasmdoc.man
  1719. Now you cam invoke the nasm man page with 'man nasm' and the nasm documentation
  1720. with 'man nasmdoc'.
  1721.  
  1722. For further information, check out the following:
  1723. Linux Assembly Language HOWTO
  1724. Linux I/O Port Programming Mini-HOWTO
  1725. Jan's Linux & Assembler HomePage (bewoner.dma.be/JanW/eng.html)
  1726.  
  1727. Also I owe a bit of thanks to Jeff Weeks at code^x software (gameprog.com/codex)
  1728. for forwarding me a couple of GAS hello-world's in the dark days before I
  1729. found Jan's page.
  1730.  
  1731.  
  1732. ::/ \::::::.
  1733. :/___\:::::::.
  1734. /|    \::::::::.
  1735. :|   _/\:::::::::.
  1736. :| _|\  \::::::::::.
  1737. :::\_____\:::::::::::...........................................ISSUE.CHALLENGE
  1738.                                       11-byte Program Displays Its Command-Line
  1739.                                       by Xbios2
  1740.  
  1741.  
  1742. The Challenge
  1743. -------------
  1744. Write an 11-byte program that displays its command line.
  1745.  
  1746.  
  1747. The Solution
  1748. ------------
  1749. Before saying that these programs won't work, try them. Some of them work only
  1750. after you've run them twice. Anyway, they' ve been tested both under Windows
  1751. and plain DOS and they work. Believe it or not, these are the first programs
  1752. I've ever written in DOS, so I just tried various ideas until some worked, even
  1753. thought I thought they wouldn't... :)
  1754.  
  1755. The command line in DOS is found in the PSP (Program Segment Prefix) which in
  1756. .COM files occupies the first 100h bytes in the segment. At offset 80h, a
  1757. <count, char> string (first byte is length of string, and n bytes follow)
  1758. contains everything typed after the filename. The last character in this string
  1759. is a CR (carriage return).
  1760.  
  1761. The requested program should be composed of three parts:
  1762.  
  1763. 1. set up pointers to data
  1764. 2. display data
  1765. 3. exit
  1766.  
  1767. Actually all the following programs DON'T include part 3, but read on. The
  1768. data (command line) can be printed either as a single string, or character by
  1769. character.
  1770.  
  1771.  
  1772. APPROACH 1: Print single string
  1773. -------------------------------
  1774. For the first approach there are two interrupts:
  1775. 1. INT 21, 9    ; write $ terminated string
  1776. 2. INT 21, 40    ; write to file using handle
  1777.  
  1778. For the first case, part 2 would be:
  1779.     mov    ah, 9
  1780.     mov    dx, 81h
  1781.     int    21h
  1782. that makes 7 bytes, leaving only 4 bytes to replace the last CR with a '$',
  1783. which are too few. (Actually, if the user would type a $ as the last character
  1784. in the comand line, this would make the smallest possible program.) The short-
  1785. est program I managed to write is:
  1786.     shr    si,1            ; D1 EE
  1787.     lodsb                ; AC
  1788.     push    si            ; 56
  1789.     add    si,ax            ; 03 F0
  1790.     mov    byte ptr [si],'$'    ; C6 04 24
  1791.     xcgh    bp,ax            ; 95
  1792.     pop    dx            ; 5A
  1793.     int    21            ; CD 21
  1794.  
  1795. For the second case, the smallest program would be this:
  1796.     ; Solution I
  1797.     mov    dx, 81h            ; BA 81 00
  1798.     mov    cl, ds:[80h]        ; 8A 0E 80 00
  1799.     mov    ah, 40h            ; B4 40
  1800.     int    21h            ; CD 21
  1801.  
  1802. The first two lines are part 1 (set up pointers) and the other two are part 2
  1803. (display string). If you think that something is missing you're right: we don't
  1804. set BX (the handle).
  1805.  
  1806.  
  1807. APPROACH 2: Print char by char
  1808. ------------------------------
  1809. For the second approach there are two interrupts:
  1810. 1. INT 21, 2    ; write char in dl
  1811. 2. INT 29    ; write char in al
  1812.  
  1813. Of course the second interrupt is better, since there is no need to load ah
  1814. with a function value. In addition, INT 29 reads the char from AL, so it can be
  1815. used together with LODSB.
  1816.  
  1817. The first way to implement this approach is to minimize part 2 (display loop).
  1818. A program that does this is the following:
  1819.     ; Solution II
  1820.     mov    si, 80h    ; BE 80 00
  1821.     lodsb        ; AC
  1822.     mov    cl, al    ; 8A C8
  1823. more:    lodsb        ; AC
  1824.     int    29h    ; CD 29
  1825.     loop    more    ; E2 FB
  1826.  
  1827. This program printed CX characters. The second way to print the string is to print up to the CR. Here is how:
  1828.     ; Solution III
  1829.     mov    si, 81h        ; BE 81 00
  1830. more:    lodsb            ; AC
  1831.     int    29h        ; CD 29
  1832.     cmp    al, 13        ; 3C 0D
  1833.     jne    more        ; 75 F9
  1834.     nop            ; 90
  1835.  
  1836. Yes, the last instruction IS a NOP. So we have an 11-byte program that works,
  1837. and even has a NOP in it. Removing the NOP creates an even crazier program that
  1838. is 10 bytes long, displays it's command line AND waits for a key press before
  1839. terminating... Actually solution II, by substituting MOV SI,80h with SHR SI,1,
  1840. does the same thing (10 bytes that display the command line and wait for the
  1841. user to press a key).
  1842.  
  1843. BTW: I really don't know why these programs work, though I have one or two
  1844. theories...
  1845.  
  1846.  
  1847. Next Issue Challenge
  1848. --------------------
  1849. Write the smallest possible PE program (win32) that outputs it's command line.
  1850.  
  1851.  
  1852. ::/ \::::::.
  1853. :/___\:::::::.
  1854. /|    \::::::::.
  1855. :|   _/\:::::::::.
  1856. :| _|\  \::::::::::.
  1857. :::\_____\:::::::::::.......................................................FIN
  1858.  
  1859.  
  1860.  
  1861.